package com.arpa.wms.controller;

import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.security.annotation.NeedLogin;
import com.arpa.wms.service.IWmsTaskService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;


/**
 * <p>
 *  task
 * </p>
 *
 * @author LEO
 * @since 2020-10-09
 */
@Log4j2
public class TaskBasicController {

    @Resource
    public RedissonClient redissonClient;

    @Autowired
    public IWmsTaskService taskService;

    /**
     * 指派作业人
     *
     * @param taskCodes 任务codes
     * @param userCode  被指派人
     * @param request
     * @return
     */
    @ApiOperation(value = "任务指派", notes = "任务指派作业人接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer "),
            @ApiImplicitParam(name = "taskCodes", value = "任务号集合", required = true, paramType = "query"),
            @ApiImplicitParam(name = "userCode", value = "被指派人标识", required = true, paramType = "query", dataType = "string"),
    })
    @NeedLogin
    @PostMapping("assign")
    public Result assign(@RequestParam(value = "taskCodes") List<String> taskCodes, @RequestParam(value = "userCode") String userCode, HttpServletRequest request) {

        log.info("任务指派:任务codes【{}】", taskCodes);
        List<RLock> locks = Lists.newArrayList();
        List<String> failCodes = Lists.newArrayList();
        StringBuilder failMsg = new StringBuilder();
        try {
            //可操作的CODE
            List<String> execCodes = Lists.newArrayList();
            for (String code : taskCodes) {
                RLock l = redissonClient.getLock(getAssignLockKey(code));
                if (l.tryLock()) {
                    log.info("任务【{}】获得锁", code);
                    execCodes.add(code);
                    locks.add(l);
                } else {
                    failCodes.add(code);
                    failMsg.append("任务【").append(code).append("】正在操作中</br>");
                    log.error("任务【{}】正在操作中", code);
                }
            }


            // 失败的订单集合
            // 线程结果集
            Map<String, Future<Boolean>> futureMap = Maps.newHashMap();
            for (String taskCode : execCodes) {
                Future<Boolean> future = taskService.assign(taskCode, userCode, UserUtil.getCode(), UserUtil.getBranchCode());
                if (future == null) {
                    failCodes.add(taskCode);
                } else {
                    futureMap.put(taskCode, future);
                }
            }
            futureMap.forEach((key, future) -> {
                try {
                    while (true) {
                        if (future.isDone()) {
                            Boolean status = future.get();
                            if (!status) {
                                failCodes.add(key);
                            }
                            break;
                        }
                    }
                } catch (ServiceException e) {
                    log.error("任务指派【{}】线程获取结果【{}】", key, e.getMessage());
                    failMsg.append(e.getMessage()).append("</br>");
                    failCodes.add(key);
                } catch (Exception e) {
                    log.error("任务指派【{}】线程获取结果【{}】", key, e.getMessage());
                    failMsg.append(e.getCause().getMessage()).append("</br>");
                    failCodes.add(key);
                }
            });
            if (failCodes.size() == 0) {
                return Result.ok("任务指派成功");
            } else {
                return Result.error(failMsg.toString());
            }
        } finally {
            try {
                locks.forEach(l -> {
                    // 判断锁不为空 && 当前线程获得到了锁
                    if (null != l && l.isLocked()) {
                        l.unlock();
                    }
                });
            } catch (Exception e) {
                log.error("释放锁失败！！", e);
            }

        }

    }


    /**
     * 取消任务指派接口
     *
     * @param taskCodes 任务codes
     * @param request
     * @return
     */
    @ApiOperation(value = "取消指派", notes = "取消任务指派接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "授权token", required = true, paramType = "header", dataType = "String", defaultValue = "Bearer 123456"),
            @ApiImplicitParam(name = "taskCodes", value = "任务号集合", required = true, paramType = "query"),
    })
    @NeedLogin
    @PostMapping("unassign")
    public Result unassign(@RequestParam(value = "taskCodes") List<String> taskCodes, HttpServletRequest request) {
        String groupCode = UserUtil.getBranchCode();
        log.info("任务取消指派:任务codes【{}】", taskCodes);
        List<RLock> locks = Lists.newArrayList();
        List<String> failCodes = Lists.newArrayList();
        StringBuilder failMsg = new StringBuilder();
        try {
            //可操作的CODE
            List<String> execCodes = Lists.newArrayList();
            for (String code : taskCodes) {
                RLock l = redissonClient.getLock(getAssignLockKey(code));
                if (l.tryLock()) {
                    log.info("任务【{}】获得锁", code);
                    execCodes.add(code);
                    locks.add(l);
                } else {
                    failCodes.add(code);
                    failMsg.append("任务【").append(code).append("】正在操作中<br>");
                    log.error("任务【{}】正在操作中", code);
                }
            }


            // 失败的订单集合
            // 线程结果集
            Map<String, Future<Boolean>> futureMap = Maps.newHashMap();
            for (String taskCode : execCodes) {
                Future<Boolean> future =
                        taskService.unassign(taskCode,groupCode);
                if (future == null) {
                    failCodes.add(taskCode);
                } else {
                    futureMap.put(taskCode, future);
                }
            }
            futureMap.forEach((key, future) -> {
                try {
                    while (true) {
                        if (future.isDone()) {
                            Boolean status = future.get();
                            if (!status) {
                                failCodes.add(key);
                            }
                            break;
                        }
                    }
                } catch (ServiceException e) {
                    log.error("任务取消指派【{}】线程获取结果【{}】", key, e.getMessage());
                    failMsg.append(e.getMessage()).append("<br>");
                    failCodes.add(key);
                } catch (Exception e) {
                    log.error("任务取消指派【{}】线程获取结果【{}】", key, e.getMessage());
                    failMsg.append(e.getCause().getMessage()).append("<br>");
                    failCodes.add(key);
                }
            });
            if (failCodes.size() == 0) {
                return Result.ok("任务取消指派成功");
            } else {
                return Result.error(failMsg.toString());
            }
        } finally {
            try {
                locks.forEach(l -> {
                    // 判断锁不为空 && 当前线程获得到了锁
                    if (null != l && l.isLocked()) {
                        l.unlock();
                    }
                });
            } catch (Exception e) {
                log.error("释放锁失败！！", e);
            }

        }

    }

    /**
     * 任务指派分布式锁
     *
     * @param code
     * @return
     */
    private String getAssignLockKey(String code) {
        return String.format("task-assign-code-%s", code);
    }


}
